{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "github地址:[https://github.com/cheesezh/python_design_patterns](https://github.com/cheesezh/python_design_patterns)\n",
    "\n",
    "## 题目\n",
    "用程序模拟以下不同情况：\n",
    "- 男人成功时，背后多半有一个伟大的女人；\n",
    "- 女人成功时，背后多半有一个失败的男人；\n",
    "- 男人失败时，闷头喝酒，谁也不用劝；\n",
    "- 女人失败时，眼泪汪汪，谁也劝不了；\n",
    "- 男人恋爱时，凡事不懂也要装逼；\n",
    "- 女人恋爱时，遇事懂也装作不懂；\n",
    "\n",
    "## 基础版本"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "男人成功时，背后多半有一个伟大的女人\n",
      "女人成功时，背后多半有一个失败的男人\n",
      "男人失败时，闷头喝酒，谁也不用劝\n",
      "女人失败时，眼泪汪汪，谁也劝不了\n",
      "男人恋爱时，凡事不懂也要装逼\n",
      "女人恋爱时，遇事懂也装作不懂\n"
     ]
    }
   ],
   "source": [
    "from abc import ABCMeta,abstractmethod\n",
    "\n",
    "\n",
    "class Person():\n",
    "    \n",
    "    __metaclass__ = ABCMeta\n",
    "    \n",
    "    def __init__(self):\n",
    "        self.action = None\n",
    "        \n",
    "    @abstractmethod\n",
    "    def get_conclusion(self):\n",
    "        pass\n",
    "    \n",
    "class Man(Person):\n",
    "    \n",
    "    def get_conclusion(self):\n",
    "        if self.action == \"成功\":\n",
    "            print(\"男人成功时，背后多半有一个伟大的女人\")\n",
    "        elif self.action == \"失败\":\n",
    "            print(\"男人失败时，闷头喝酒，谁也不用劝\")\n",
    "        elif self.action == \"恋爱\":\n",
    "            print(\"男人恋爱时，凡事不懂也要装逼\")\n",
    "            \n",
    "            \n",
    "class Woman(Person):\n",
    "    \n",
    "    def get_conclusion(self):\n",
    "        if self.action == \"成功\":\n",
    "            print(\"女人成功时，背后多半有一个失败的男人\")\n",
    "        elif self.action == \"失败\":\n",
    "            print(\"女人失败时，眼泪汪汪，谁也劝不了\")\n",
    "        elif self.action == \"恋爱\":\n",
    "            print(\"女人恋爱时，遇事懂也装作不懂\")\n",
    "\n",
    "\n",
    "def main():\n",
    "    \n",
    "    persons = []\n",
    "    man1 = Man()\n",
    "    man1.action = \"成功\"\n",
    "    persons.append(man1)\n",
    "    woman1 = Woman()\n",
    "    woman1.action = \"成功\"\n",
    "    persons.append(woman1)\n",
    "    \n",
    "    man2 = Man()\n",
    "    man2.action = \"失败\"\n",
    "    persons.append(man2)\n",
    "    woman2 = Woman()\n",
    "    woman2.action = \"失败\"\n",
    "    persons.append(woman2)\n",
    "    \n",
    "    man3 = Man()\n",
    "    man3.action = \"恋爱\"\n",
    "    persons.append(man3)\n",
    "    woman3 = Woman()\n",
    "    woman3.action = \"恋爱\"\n",
    "    persons.append(woman3)\n",
    "    \n",
    "    for p in persons:\n",
    "        p.get_conclusion()\n",
    "        \n",
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 点评\n",
    "\n",
    "上述代码算是初步的面向对象编程，但是在“Man”和“Woman”中那些“if-else”判断很复杂，如果再增加“结婚”状态，那需要增加分支判断。\n",
    "\n",
    "这里我们可以使用访问者模式。\n",
    "\n",
    "## 访问者模式\n",
    "\n",
    "访问者模式，表示一个作用于某对象结构中各元素的操作。它使你可以在不改变各元素的类的前提下，定义作用于这些元素的新操作。[DP]\n",
    "\n",
    "访问者模式的基本代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Visitor1 visit element A\n",
      "Visitor1 visit element B\n",
      "Visitor2 visit element A\n",
      "Visitor2 visit element B\n"
     ]
    }
   ],
   "source": [
    "from abc import ABCMeta, abstractmethod\n",
    "\n",
    "\n",
    "class Visitor():\n",
    "    \"\"\"\n",
    "    Visitor类，为该对象结构中ConcretElement的每一个类声明一个Visit操作。\n",
    "    \"\"\"\n",
    "    __metaclass__ = ABCMeta\n",
    "    \n",
    "    @abstractmethod\n",
    "    def visit_concret_element_a(self, concret_element_a):\n",
    "        pass\n",
    "    \n",
    "    @abstractmethod\n",
    "    def visit_concret_element_b(self, concret_element_b):\n",
    "        pass\n",
    "    \n",
    "    \n",
    "class ConcretVisitor1(Visitor):\n",
    "    \"\"\"\n",
    "    ConcretVisitor类，具体访问者，实现每个由Visitor声明的操作。每个操作实现算法的一部分，\n",
    "    而该算法片段乃是对应于结构中对象的类。\n",
    "    \"\"\"\n",
    "    def visit_concret_element_a(self, concret_element_a):\n",
    "        print(\"Visitor1 visit element A\")\n",
    "        \n",
    "    def visit_concret_element_b(self, concret_element_b):\n",
    "        print(\"Visitor1 visit element B\")\n",
    "        \n",
    "    \n",
    "class ConcretVisitor2(Visitor):\n",
    "    \"\"\"\n",
    "    同上\n",
    "    \"\"\"\n",
    "    def visit_concret_element_a(self, concret_element_a):\n",
    "        print(\"Visitor2 visit element A\")\n",
    "        \n",
    "    def visit_concret_element_b(self, concret_element_b):\n",
    "        print(\"Visitor2 visit element B\")\n",
    "        \n",
    "        \n",
    "class Element():\n",
    "    \"\"\"\n",
    "    Element类，定义一个Accept操作，它以一个访问者为参数\n",
    "    \"\"\"\n",
    "    __metaclass__ = ABCMeta\n",
    "    \n",
    "    @abstractmethod\n",
    "    def accept(self, visitor):\n",
    "        pass\n",
    "    \n",
    "    \n",
    "class ConcretElementA(Element):\n",
    "    \"\"\"\n",
    "    具体元素类，实现Accept操作\n",
    "    \"\"\"\n",
    "    def accept(self, visitor):\n",
    "        visitor.visit_concret_element_a(self)\n",
    "        \n",
    "    def other_functions_a(self):\n",
    "        pass\n",
    "    \n",
    "\n",
    "class ConcretElementB(Element):\n",
    "    \"\"\"\n",
    "    具体元素类，实现Accept操作\n",
    "    \"\"\"\n",
    "    def accept(self, visitor):\n",
    "        visitor.visit_concret_element_b(self)\n",
    "        \n",
    "    def other_functions_b(self):\n",
    "        pass\n",
    "    \n",
    "    \n",
    "class ObjectStructure():\n",
    "    \"\"\"\n",
    "    ObjectStructure类，能枚举他的元素，可以提供一个高层的接口以允许访问者访问它的元素。\n",
    "    \"\"\"\n",
    "    def __init__(self):\n",
    "        self.elements = []\n",
    "        \n",
    "    def accept(self, visitor):\n",
    "        for element in self.elements:\n",
    "            element.accept(visitor)\n",
    "            \n",
    "            \n",
    "def main():\n",
    "    o = ObjectStructure()\n",
    "    o.elements.append(ConcretElementA())\n",
    "    o.elements.append(ConcretElementB())\n",
    "    \n",
    "    v1 = ConcretVisitor1()\n",
    "    v2 = ConcretVisitor2()\n",
    "    \n",
    "    o.accept(v1)\n",
    "    o.accept(v2)\n",
    "    \n",
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 改进版本——访问者模式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "男人成功时，背后多半有一个伟大的女人\n",
      "女人成功时，背后多半有一个失败的男人\n",
      "----------\n",
      "男人失败时，闷头喝酒，谁也不用劝\n",
      "女人失败时，眼泪汪汪，谁也劝不了\n",
      "----------\n",
      "男人恋爱时，凡事不懂也要装逼\n",
      "女人恋爱时，遇事懂也装作不懂\n",
      "\n",
      "由于使用了双分派技术，当新增`结婚`状态时，只需要新增一个状态子类，然后修改客户端即可。\n",
      "\n",
      "男人成功时，背后多半有一个伟大的女人\n",
      "女人成功时，背后多半有一个失败的男人\n",
      "----------\n",
      "男人失败时，闷头喝酒，谁也不用劝\n",
      "女人失败时，眼泪汪汪，谁也劝不了\n",
      "----------\n",
      "男人恋爱时，凡事不懂也要装逼\n",
      "女人恋爱时，遇事懂也装作不懂\n",
      "----------\n",
      "男人结婚时，有妻徒刑遥无期\n",
      "女人结婚时，婚姻保险保平安\n"
     ]
    }
   ],
   "source": [
    "from abc import ABCMeta, abstractmethod\n",
    "\n",
    "\n",
    "class Action():\n",
    "    \"\"\"\n",
    "    状态抽象类，Visitor类，为该对象结构中ConcretElement的每一个类声明一个Visit操作。\n",
    "    \"\"\"\n",
    "    __metaclass__ = ABCMeta\n",
    "    \n",
    "    @abstractmethod\n",
    "    def get_man_conclusion(self, man):\n",
    "        pass\n",
    "    \n",
    "    @abstractmethod\n",
    "    def get_woman_conclusion(self, woman):\n",
    "        pass\n",
    "    \n",
    "    \n",
    "class Success(Action):\n",
    "    \"\"\"\n",
    "    成功状态类，ConcretVisitor类，具体访问者，实现每个由Visitor声明的操作。每个操作实现算法的一部分，\n",
    "    而该算法片段乃是对应于结构中对象的类。\n",
    "    \"\"\"\n",
    "    def get_man_conclusion(self, man):\n",
    "        print(\"男人成功时，背后多半有一个伟大的女人\")\n",
    "        \n",
    "    def get_woman_conclusion(self, woman):\n",
    "        print(\"女人成功时，背后多半有一个失败的男人\")\n",
    "        \n",
    "    \n",
    "class Failing(Action):\n",
    "    \"\"\"\n",
    "    失败状态类，ConcretVisitor类，具体访问者，实现每个由Visitor声明的操作。每个操作实现算法的一部分，\n",
    "    而该算法片段乃是对应于结构中对象的类。\n",
    "    \"\"\"\n",
    "    def get_man_conclusion(self, man):\n",
    "        print(\"男人失败时，闷头喝酒，谁也不用劝\")\n",
    "        \n",
    "    def get_woman_conclusion(self, woman):\n",
    "        print(\"女人失败时，眼泪汪汪，谁也劝不了\")\n",
    "        \n",
    "        \n",
    "class Amativeness(Action):\n",
    "    \"\"\"\n",
    "    恋爱状态类，ConcretVisitor类，具体访问者，实现每个由Visitor声明的操作。每个操作实现算法的一部分，\n",
    "    而该算法片段乃是对应于结构中对象的类。\n",
    "    \"\"\"\n",
    "    def get_man_conclusion(self, man):\n",
    "        print(\"男人恋爱时，凡事不懂也要装逼\")\n",
    "        \n",
    "    def get_woman_conclusion(self, woman):\n",
    "        print(\"女人恋爱时，遇事懂也装作不懂\")\n",
    "        \n",
    "        \n",
    "        \n",
    "class Person():\n",
    "    \"\"\"\n",
    "    人抽象类，Element类，定义一个Accept操作，它以一个访问者为参数\n",
    "    \"\"\"\n",
    "    __metaclass__ = ABCMeta\n",
    "    \n",
    "    @abstractmethod\n",
    "    def accept(self, visitor):\n",
    "        pass\n",
    "    \n",
    "    \n",
    "class Man(Person):\n",
    "    \"\"\"\n",
    "    男人类，具体元素类，实现Accept操作\n",
    "    \"\"\"\n",
    "    def accept(self, visitor):\n",
    "        \"\"\"\n",
    "        双分派技术：\n",
    "        第一次分派：首先在客户端程序中将具体状态作为参数传递给“男人”类完成一次分派；\n",
    "        第二次分派：然后“男人”类调用作为参数的“具体状态”中的方法“男人反应”，同事将自己(self)作为参数传递进去；\n",
    "        \"\"\"\n",
    "        visitor.get_man_conclusion(self)\n",
    "        \n",
    "    def other_functions_a(self):\n",
    "        pass\n",
    "    \n",
    "\n",
    "class Woman(Person):\n",
    "    \"\"\"\n",
    "    女人类，具体元素类，实现Accept操作\n",
    "    \"\"\"\n",
    "    def accept(self, visitor):\n",
    "        visitor.get_woman_conclusion(self)\n",
    "        \n",
    "    def other_functions_b(self):\n",
    "        pass\n",
    "    \n",
    "    \n",
    "class ObjectStructure():\n",
    "    \"\"\"\n",
    "    ObjectStructure类，能枚举他的元素，可以提供一个高层的接口以允许访问者访问它的元素。\n",
    "    \"\"\"\n",
    "    def __init__(self):\n",
    "        self.elements = []\n",
    "        \n",
    "    def accept(self, visitor):\n",
    "        for element in self.elements:\n",
    "            element.accept(visitor)\n",
    "            \n",
    "            \n",
    "def main():\n",
    "    o = ObjectStructure()\n",
    "    o.elements.append(Man())\n",
    "    o.elements.append(Woman())\n",
    "    \n",
    "    v1 = Success()\n",
    "    v2 = Failing()\n",
    "    v3 = Amativeness()\n",
    "    \n",
    "    o.accept(v1)\n",
    "    print(\"-\"*10)\n",
    "    o.accept(v2)\n",
    "    print(\"-\"*10)\n",
    "    o.accept(v3)\n",
    "    \n",
    "main()\n",
    "\n",
    "print()\n",
    "print(\"由于使用了双分派技术，当新增`结婚`状态时，只需要新增一个状态子类，然后修改客户端即可。\")\n",
    "print()\n",
    "\n",
    "class Marriage(Action):\n",
    "    \n",
    "    def get_man_conclusion(self, man):\n",
    "        print(\"男人结婚时，有妻徒刑遥无期\")\n",
    "        \n",
    "    def get_woman_conclusion(self, woman):\n",
    "        print(\"女人结婚时，婚姻保险保平安\")\n",
    "        \n",
    "def main():\n",
    "    o = ObjectStructure()\n",
    "    o.elements.append(Man())\n",
    "    o.elements.append(Woman())\n",
    "    \n",
    "    v1 = Success()\n",
    "    v2 = Failing()\n",
    "    v3 = Amativeness()\n",
    "    v4 = Marriage()\n",
    "    \n",
    "    o.accept(v1)\n",
    "    print(\"-\"*10)\n",
    "    o.accept(v2)\n",
    "    print(\"-\"*10)\n",
    "    o.accept(v3)\n",
    "    print(\"-\"*10)\n",
    "    o.accept(v4)\n",
    "    \n",
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 点评\n",
    "\n",
    "访问者模式适用于数据结构相对稳定的系统，它把数据结构和作用于数据结构上的操作之间的耦合解脱开，使得操作结合可以相对自由地演化。\n",
    "\n",
    "访问这模式的目的是要把`处理`从`数据结构`分离出来。很多系统可以按照算法和数据结构分开，如果这样的系统有比较稳定的数据结构，又有易于变化的算法的话，那就比较适合使用访问者模式，因为访问者模式使得算法操作的增加变得容易。反之，如果系统的数据结构对象易于变化，经常要有新的数据对象增加进来，就不适合使用访问者模式。\n",
    "\n",
    "访问者模式的优点就是增加新的操作很容易，因为增加新的操作就意味着增加一个新的访问者。访问者模式将有关的行为集中到一个访问者对象中。\n",
    "\n",
    "访问者模式的缺点其实就是增加新的数据结构变得困难了。\n",
    "\n",
    "GoF四人中的一个作者说过，大多数时候并不需要访问者模式，但当一旦需要访问者模式时，那就是真的需要它了。因为我们很难找到数据结构不变化的情况，所以用访问者模式的机会也就不太多。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
